fetch_twin_metrics()

Домен: SIMULATION | Контур: Сбор данных пользователя при холодном старте

WarningОграничение публичной документации

В открытом доступе представлена демонстрационная версия метода. В настоящей публичной документации отображены не все шаги, технические сценарии и приватные эндпоинты для системы цифровых симуляторов бизнес-процессов.

  • Полная спецификация метода: Будет доступна только во внутреннем контуре разработки (Confluence / Swagger Enterprise).

1. Бизнес-спецификация метода

  • Идентификатор метода: BPDS-SIM-M04
  • Системное имя: fetch_twin_metrics()
  • Микросервис: simulation-core-engine
  • Домен: SIMULATION
  • Класс / Компонент: simulation.engine.registry.TickRegistryImpl

1.1. Описание логики работы

Этот метод отвечает за наполнение оперативной памяти симулятора данными о пользователе, если их там не оказалось (ситуация Cache Miss). Метод работает как параллельный «пылесос» данных. Чтобы виртуальный поток пользователя не простаивал, симулятор одновременно запускает четыре независимых запроса: один улетает по сети в реальное приложение (бэкенд) за списком продуктов в холодильнике, а остальные три идут во внутреннюю базу данных симулятора за личными привычками, весами и историей голода.

1.2. Пошаговое выполнение

  1. Проверка памяти: Метод заглядывает в оперативную память (ConcurrentHashMap). Если данные пользователя там уже есть, он мгновенно возвращает их, завершая работу.
  2. Параллельный старт (Если в RAM пусто): Симулятор запускает асинхронный каскад из четырех одновременных запросов:
    • Запрос 1 (Сетевой): HTTP-запрос к API приложения для получения списка продуктов в холодильнике бэкенда.
    • Запрос 2 (База данных): SQL-выборка текущих личных весов пользователя из таблицы twin_weight_preferences.
    • Запрос 3 (База данных): SQL-выборка черт характера (коэффициент лени) из таблицы twin_profiles.
    • Запрос 4 (База данных): SQL-выборка времени последнего приема пищи из таблицы twin_state_snapshots.
  3. Ожидание и Агрегация: Поток симуляции дожидается ответов от всех четырех источников, объединяет их в один сырой пакет данных (RawExtractionPacket) и передает его следующему методу (Метод 5) для расчета голода.

2. Диаграмма последовательности метода (Вход и Выход флоу)

Диаграмма наглядно показывает, как метод при отсутствии данных в оперативной памяти параллельно штурмует сетевой API-шлюз приложения и три независимые таблицы PostgreSQL симулятора.

sequenceDiagram
    autonumber
    participant Core as Метод 3 (CoreSimulationEngine)
    participant Reg as TickRegistryImpl
    participant Map as Оперативная память (RAM кэш)
    participant API as API Приложения (Black-Box бэкенд)
    participant DB as База Данных Симулятора

    %% ВХОД МЕТОДА И ПРОВЕРКА КЭША
    Core->>Reg: Вызов fetchTwinMetrics(twin_id, tick)
    activate Reg
    Note over Reg: Вход метода: twin_id и номер текущего такта
    
    Reg->>Map: Проверка: cache.get(twin_id)
    Map-->>Reg: Результат: null (В памяти пусто)

    %% ПАРАЛЛЕЛЬНЫЙ СБОР ДАННЫХ
    Note over Reg: Запуск 4-х параллельных потоков ввода-вывода (CompletableFuture)
    
    par Запрос 1: Внешнее API приложения по сети
        Reg->>API: HTTP GET /api/v1/fridge/products (Заголовок X-Twin-ID)
        API-->>Reg: Ответ: JSON (Список продуктов в холодильнике)
    and Запрос 2: Внутренняя БД симулятора (Личные веса)
        Reg->>DB: SQL SELECT FROM twin_weight_preferences WHERE twin_id = ?
        DB-->>Reg: Ответ: Набор строк (категория, личный вес K)
    and Запрос 3: Внутренняя БД симулятора (Характер и Лень)
        Reg->>DB: SQL SELECT FROM twin_profiles WHERE twin_id = ?
        DB-->>Reg: Ответ: Строка профиля (laziness_coefficient)
    and Запрос 4: Внутренняя БД симулятора (История еды)
        Reg->>DB: SQL SELECT FROM twin_state_snapshots WHERE twin_id = ?
        DB-->>Reg: Ответ: Строка снапшота (last_tick_updated)
    end

    %% ОБЪЕДИНЕНИЕ И ПЕРЕДАЧА УПРАВЛЕНИЯ
    Note over Reg: Сборка результатов в единый пакет RawExtractionPacket
    Note over Reg: Выход метода: Передача сырого пакета на шаг аналитической свертки
    
    Reg-->>Core: Вызов метода 5 compileAndCommitMetrics(twin_id, tick, packet)
    deactivate Reg


3. Схемы данных и SQL-взаимодействие

Этот метод выполняет три точечных SQL-запроса на чтение к внутренней базе данных симулятора.

3.1. Запрос к таблице личных весов пользователя (Запрос 2)

SELECT category_id, preference_weight_k 
FROM simulation.twin_weight_preferences 
WHERE twin_id = ?::uuid;

3.2. Запрос к таблице характера и лени (Запрос 3)

SELECT chronotype, laziness_coefficient 
FROM simulation.twin_profiles 
WHERE twin_id = ?::uuid;

3.3. Запрос к таблице истории еды (Запрос 4)

SELECT hunger_x0, last_tick_updated 
FROM simulation.twin_state_snapshots 
WHERE twin_id = ?::uuid;

4. Спецификация обмена данными (Вход / Выход и Сетевые контракты)

Метод выполняет один внешний сетевой HTTP-запрос к приложению (бэкенду) в режиме «черного ящика».

4.1. Сетевой HTTP-запрос к приложению (Запрос 1)

GET /api/v1/fridge/products HTTP/1.1
Host: almaty-backend-gateway:8080
X-Shop-Token: almaty_retail_secret_hash_2026
X-Twin-ID: d3b07384-d113-4956-bf8a-e421cd7bf777
X-Request-ID: req-t105-active-d3b07384
Accept: application/json

4.2. Сетевой HTTP-ответ от приложения (Данные для сборки пакета)

Бэкенд возвращает актуальный список продуктов, которые прямо сейчас физически лежат в его базе данных:

[
  {
    "fridge_item_id": "3cb294dd-e011-4f11-b552-111122223333",
    "sku": "BANANA_ECUADOR_KG",
    "category_id": "FRUITS",
    "quantity": 1.6950,
    "unit": "kg",
    "added_at": "2026-08-08T18:30:00Z"
  }
]

5. ЗАДАЧА ДЛЯ РАЗРАБОТЧИКА: BACKEND

Заголовок: Реализация неблокирующего контура параллельного сбора данных fetch_twin_metrics

5.1. Что нужно сделать

  1. Написать Java-класс TickRegistryImpl в пакете simulation.engine.registry, реализующий интерфейс TickRegistry. Пометить аннотацией @Component.
  2. Внедрить в класс карту оперативной памяти ConcurrentHashMap<UUID, TwinContext>.
  3. Настроить параллельное выполнение четырех запросов с использованием CompletableFuture.supplyAsync().
  4. В качестве исполнителя асинхронных задач передать пул виртуальных потоков Java (Executors.newVirtualThreadPerTaskExecutor()), чтобы сетевые ожидания (I/O Wait) не блокировали физические ядра процессора.
  5. Для отправки HTTP-запроса к приложению использовать встроенный Spring RestClient или WebClient, передавая обязательные заголовки X-Twin-ID и X-Request-ID.
  6. Собрать результаты всех четырех асинхронных задач через метод CompletableFuture.allOf().thenApply() в один общий объект-рекорд RawExtractionPacket и передать его во внутренний метод обработки (Метод 5).

6. ЗАДАЧА ДЛЯ РАЗРАБОТЧИКА: МИГРАЦИЯ

Заголовок: Индексация таблиц snapshots и preferences для ускорения параллельного чтения

6.1. Что нужно сделать

Когда 1500 виртуальных потоков пользователей одновременно запустят этот метод при старте, база данных симулятора получит резкий шквал параллельных SQL-запросов по ключу twin_id. Чтобы база данных не зависла, необходимо наложить B-Tree индексы на таблицы истории и личных весов симулятора.

-- Индекс для быстрого поиска истории приема пищи твина
CREATE UNIQUE INDEX IF NOT EXISTS idx_twin_snapshots_twin_id_lookup 
ON simulation.twin_state_snapshots (twin_id);

-- Индекс для быстрой выгрузки карты личных весов твина
CREATE INDEX IF NOT EXISTS idx_twin_weights_twin_id_batch_lookup
ON simulation.twin_weight_preferences (twin_id);